﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Fraction
{

    public class ZeroDenominatorException : Exception
    {
        public ZeroDenominatorException() {}

        public ZeroDenominatorException(string msg) : base(msg) { }

        public ZeroDenominatorException(string msg, Exception e) : base(msg, e) { }
    }
    /// <summary>
    /// Immutable
    /// </summary>
    public class Fraction
    {
        public int Numerator { get; private set; }
        public int Denominator { get; private set; }

        private Fraction reduced;

        public static Fraction ReducedFraction(Fraction f)
        {
            int min = Math.Min(f.Numerator, f.Denominator);
            int max = Math.Max(f.Numerator, f.Denominator);

            // Euclidean alg
            while (max%min != 0)
            {
                int t = max % min;
                max = min;
                min = t;
            }
            return new Fraction(f.Numerator / min, f.Denominator / min, true);
        }

        private Fraction(int n, int d, bool reduced)
        {
            if (d == 0)
                throw new ArgumentOutOfRangeException("Denominator may not be 0");
            Numerator = n;
            Denominator = d;
        }

        public Fraction(int n = 0, int d = 1) // default arg
        {         
            Numerator = n;
            Denominator = d;
            if ( d == 0)
                throw new ZeroDenominatorException(this.ToString());

            //reduced = ReducedFraction(this);
        }

        public static Fraction operator *(Fraction f1, Fraction f2)
        {
            return new Fraction(f1.Numerator * f2.Numerator,
                                f1.Denominator * f2.Denominator);
        }

        public static Fraction operator /(Fraction f1, Fraction f2)
        {
            return f1 * ~f2;
        }

        public static bool operator==(Fraction f1, Fraction f2)
        {
            if ( (Object)f1 == null ^ (Object)f2 == null)
                return false;
            if ((Object)f1 == null && (Object)f2 == null)
                return true; 
            return f1.Numerator == f2.Numerator &&
                   f1.Denominator == f2.Denominator;
        }

        public static bool operator !=(Fraction f1, Fraction f2)
        {
            return !(f1 == f2);
        }

        public static Fraction operator -(Fraction f)
        {
            return new Fraction(-f.Numerator, f.Denominator);
        }


        public static Fraction operator ~(Fraction f)
        {
            //Thread.Sleep(5000);
            return new Fraction(f.Denominator, f.Numerator);
        }


        public static Fraction operator --(Fraction f)
        {
            return new Fraction(f.Numerator - f.Denominator, f.Denominator);
        }

        public bool Equals(Fraction f)
        {
            return this.Numerator == f.Numerator &&
                   this.Denominator == f.Denominator;
        }

        public override String ToString()
        {
            return string.Format("{0} / {1}", Numerator, Denominator);
        }
        
    }
}
